//
//  OIMemoryWindowController.m
//  OrchIDE
//
//  Created by local22 on 8/13/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "OIMemoryWindowController.h"
#import "OIEmulator.h"
#import "emulator.h"
#import "instruction.h"

static int OIMemoryWindowControllerObservationContextTrackingRegister = 101;

@interface OIEmulator (OIMemoryWindowExtensions)
- (void)readMemoryFromCodeFile:(NSString *)path;
@end

@implementation OIEmulator (OIMemoryWindowExtensions)
- (void)readMemoryFromCodeFile:(NSString *)path
{
	FILE *f = fopen([path fileSystemRepresentation], "r");
	if(!f) {
		return;
	}

	//TODO: we want this to be initialized to [_tableView selectedRow] - _addressRange.location
	//but these variables are not in scope for this method.
	int32_t paddr = 0;

	int16_t data;
	while(!feof(f)) {
		fscanf(f, " #%*[^\n]");
		if(fscanf(f, "%hx #%*[^\n]", &data) >= 1) {
			[self setValue:data atPhysicalAddress:paddr];
			paddr++;
		}
		int whitespace = getc(f);
		if(whitespace == EOF) break;
		if(!isspace(whitespace)) {
			//TODO: have ben make this pop up an error
			//Unexpected character %c, whitespace
			//Call undo() to roll back this change
			break;
		}
	}
	
	fclose(f);
}
@end


@implementation OIMemoryWindowController
- (id)initWithWindow:(NSWindow *)window
{
	self = [super initWithWindow:window];
	if(!self) {
		return nil;
	}
	
	[self setAddressRange:NSMakeRange(0, MEMORY_SIZE)];

	return self;
}

- (void)dealloc
{
	[self setEmulator:nil];
	[_tableView setDataSource:nil];
	
	[super dealloc];
}

- (NSString *)windowNibName
{
	return @"OIMemory";
}

- (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName
{
	if(_trackingRegister) {
		return [NSString stringWithFormat:@"%@ - %04x to %04x - %@", displayName, _addressRange.location, NSMaxRange(_addressRange) - 1, _trackingRegister];
	} else {
		return [NSString stringWithFormat:@"%@ - %04x to %04x", displayName, _addressRange.location, NSMaxRange(_addressRange) - 1];
	}
}

#pragma mark Accessors
- (NSRange)addressRange
{
	return _addressRange;
}

- (void)setAddressRange:(NSRange)addressRange
{
	_addressRange = addressRange;
	[_tableView reloadData];
	[self synchronizeWindowTitleWithDocumentName];
}

- (void)setEmulator:(OIEmulator *)emulator
{
	if(emulator == _emulator) {
		return;
	}

	if(_emulator) {
		[[NSNotificationCenter defaultCenter] removeObserver:self name:OIEmulatorDidWriteMemoryNotificationName object:_emulator];
	}

	if(_trackingRegister) {
		[_emulator removeObserver:self forKeyPath:_trackingRegister];
	}
	
	[_emulator release];
	_emulator = [emulator retain];

	if(_trackingRegister) {
		[_emulator addObserver:self forKeyPath:_trackingRegister options:NSKeyValueObservingOptionNew context:(void *)&OIMemoryWindowControllerObservationContextTrackingRegister];
	}
	
	if(_emulator) {
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(emulatorDidWriteMemory:) name:OIEmulatorDidWriteMemoryNotificationName object:_emulator];
	}
}

- (void)setTrackingRegister:(NSString *)trackingRegister
{
	if(trackingRegister == _trackingRegister) {
		return;
	}
	
	[_trackingRegister release];
	_trackingRegister = [trackingRegister retain];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == &OIMemoryWindowControllerObservationContextTrackingRegister) {
		int16_t registerValue = [[change objectForKey:NSKeyValueChangeNewKey] intValue];
		int32_t registerAddress = *(uint16_t *)&registerValue;
		[self scrollToAddress:registerAddress];
	}
}


#pragma mark Actions
- (void)scrollToAddress:(int32_t)address
{
	if(!NSLocationInRange(address, _addressRange)) {
		return;
	}
	
	NSInteger rowIndex = address - _addressRange.location;
	[self window];
	[_tableView scrollRowToVisible:rowIndex];
	[_tableView selectRow:rowIndex byExtendingSelection:NO];
}

- (IBAction)showGotoPanel:(id)sender
{
	int32_t selectedAddress = [_tableView selectedRow] - _addressRange.location;
	[_addressField setObjectValue:[NSNumber numberWithInt:selectedAddress]];
	[[NSApplication sharedApplication] beginSheet:_gotoPanel modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(gotoPanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
}

- (IBAction)closeGotoPanel:(id)sender
{
	[[NSApplication sharedApplication] endSheet:_gotoPanel returnCode:NSOKButton];
}

- (IBAction)cancelGotoPanel:(id)sender
{
	[[NSApplication sharedApplication] endSheet:_gotoPanel returnCode:NSCancelButton];
}

- (void)gotoPanelDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
	[sheet orderOut:nil];
	if(returnCode == NSOKButton) {
		[self scrollToAddress:[_addressField intValue]];
	}
}

- (IBAction)placeCodeFile:(id)sender
{
	NSOpenPanel *panel = [NSOpenPanel openPanel];
	[panel setTitle:@"Place M-file"];
	[panel beginSheetForDirectory:nil file:nil modalForWindow:[self window] modalDelegate:self didEndSelector:@selector(placeCodeFileSheetDidEnd:returnCode:contextInfo:) contextInfo:nil];
}

- (void)placeCodeFileSheetDidEnd:(NSOpenPanel *)openPanel returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
	if(returnCode == NSOKButton) {
		[_emulator readMemoryFromCodeFile:[openPanel filename]];
	}
}

#pragma mark Notifications
- (void)emulatorDidWriteMemory:(NSNotification *)aNotification
{
	NSArray *addresses = [[aNotification userInfo] objectForKey:@"addresses"];
	BOOL hasAddressInRange = NO;
	for(NSNumber *address in addresses) {
		if(NSLocationInRange([address intValue], _addressRange)) {
			hasAddressInRange = YES;
			break;
		}
	}

	if(hasAddressInRange) {
		[_tableView reloadData];
	}
}

#pragma mark Table view data source
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
	return _addressRange.length;
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
	if([[tableColumn identifier] isEqualToString:@"physicalAddress"]) {
		return [NSNumber numberWithInt:_addressRange.location + row];
	} else if([[tableColumn identifier] isEqualToString:@"value"]) {
		return [NSNumber numberWithInt:[_emulator valueAtPhysicalAddress:_addressRange.location + row]];
	} else if([[tableColumn identifier] isEqualToString:@"disassembly"]) {
		union instruction i;
		i.i = [_emulator valueAtPhysicalAddress:_addressRange.location + row];
		uint16_t buffer[32];
		int16_t length;
		disassemble_instruction(buffer, &length, i);
		return [NSString stringWithCharacters:buffer length:length];
	}
	
	return nil;
}

- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
	if([[tableColumn identifier] isEqualToString:@"value"]) {
		[_emulator setValue:[object intValue] atPhysicalAddress:_addressRange.location + row];
	}
}

@end
